import axios, { type AxiosError, type AxiosInstance, type AxiosRequestHeaders, type AxiosResponse } from 'axios'
import { setting, axiosVariable, type CustomInternalAxiosRequestConfig, requestFieldConvert } from './setting'
import { startLoading, endLoading } from './loading'
import { cacheRequest, removePending } from './cancelToken'
import { storeToRefs } from 'pinia'
import { useAppStoreWithOut } from '@/store/modules/app'
import { useUserStoreWithOut } from '@/store/modules/user'
import { getAccessToken, getTenantIdCache, getRefreshToken, setToken } from '@/utils/auth'
import router from '@/router'
import { ElMessage, ElMessageBox, ElNotification } from 'element-plus'
import qs from 'qs'
// 引入日志插件
import Logger from '@/utils/Logger'
import { AuthApi } from '@/api/auth'
const { VITE_APP_TENANT_ENABLE } = import.meta.env

// 创建axios实例
export const service: AxiosInstance = axios.create({
  baseURL: setting.base_url,
  timeout: setting.request_timeout, // 请求超时时间
  withCredentials: false, // 禁用 Cookie 等信息
  headers: {
    'Content-Type': setting.default_headers
  }
})

// request拦截器
service.interceptors.request.use(
  (config: CustomInternalAxiosRequestConfig) => {
    const appStore = useAppStoreWithOut()
    const { systemConfig } = storeToRefs(appStore)
    let isToken = setting.whiteList.some(v => {
      if (config.url && config.url.indexOf(v) > -1) {
        return false
      }
    })
    if (getAccessToken() && !isToken) {
      ;(config as Recordable).headers.Authorization = 'Bearer ' + getAccessToken() // 让每个请求携带自定义token
    }
    // 设置租户
    if (VITE_APP_TENANT_ENABLE && VITE_APP_TENANT_ENABLE === 'true') {
      const tenantId = getTenantIdCache()
      if (tenantId) (config as Recordable).headers['tenant-id'] = tenantId
    }
    // 是否使用mock模拟的后端接口
    if (config.is_mock) {
      config.baseURL = ''
    }
    // loading功能启用
    if ((systemConfig.value.loadingType === 'body' || systemConfig.value.loadingType === 'content') && config.is_loading !== false) {
      startLoading()
    }
    // 请求参数处理
    const params = config.params || {}
    const data = config.data || false
    if (config.method?.toUpperCase() === 'POST' && (config.headers as AxiosRequestHeaders)['Content-Type'] === 'application/x-www-form-urlencoded') {
      config.data = qs.stringify(data)
    }
    // get参数编码
    if (config.method?.toUpperCase() === 'GET' && params) {
      config.paramsSerializer = param => qs.stringify(param, { allowDots: true, indices: false })
      // const filterParams = Object.keys(params)
      //   .filter(key => params[key] !== undefined && ((Array.isArray(params[key]) && params[key].length > 0) || !Array.isArray(params[key])))
      //   .reduce((acc, key) => {
      //     acc[key] = params[key]
      //     return acc
      //   }, {})
      // const paramsStr = qs.stringify(filterParams, { allowDots: true, indices: false })
      // if (paramsStr) {
      //   config.url = config.url + '?' + paramsStr
      // }
    }
    // 缓存请求
    cacheRequest(config)
    return config
  },
  // 请求报错
  (error: AxiosError) => {
    const appStore = useAppStoreWithOut()
    const { systemConfig } = storeToRefs(appStore)
    // Do something with request error
    // 当前请求出错，当前请求加1的loading也需要减掉
    const config: CustomInternalAxiosRequestConfig | any = error.config
    if (systemConfig.value && (systemConfig.value.loadingType === 'body' || systemConfig.value.loadingType === 'content') && config && config.is_loading !== false) {
      endLoading()
    }
    removePending(config)
    Logger.error('请求拦截error：', error) // for debug
    return Promise.reject(error)
  }
)

// response 拦截器
service.interceptors.response.use(
  async (response: AxiosResponse<any>) => {
    const appStore = useAppStoreWithOut()
    const { systemConfig } = storeToRefs(appStore)
    let { data } = response
    const config: CustomInternalAxiosRequestConfig = response.config
    // 增加延时，相同请求再完成后，不得在短时间内重复请求
    setTimeout(() => {
      removePending(config)
    }, 500)
    if (systemConfig.value && (systemConfig.value.loadingType === 'body' || systemConfig.value.loadingType === 'content') && config.is_loading !== false) {
      endLoading()
    }
    if (!data) {
      // 返回“[HTTP]请求没有返回值”;
      throw new Error()
    }
    const { t } = useI18n()
    // 二进制数据则直接返回，例如说 Excel 导出
    if (response.request.responseType === 'blob' || response.request.responseType === 'arraybuffer') {
      // 注意：如果导出的响应为 json，说明可能失败了，不直接返回进行下载
      if (response.data.type !== 'application/json') {
        return response.data
      }
      data = await new Response(response.data).json()
    }
    // 后端状态字段转化为前端状态字段
    requestFieldConvert(data)
    console.log('请求响应处理前：', data)
    // 未设置状态码则默认成功状态
    const code = data[setting.result_code_key] || setting.result_success_code
    // 获取错误信息
    const msg = data[setting.result_msg_key] || t('error', 'unknownMistake')
    // 请求结果1： 成功
    if (code === setting.result_success_code) {
      console.log('请求响应：', data)
      return data
      // 请求结果2： 未认证
    } else if (code === setting.result_auth_code) {
      // 如果未认证，程序有刷新token功能，说明可能是访问令牌过期了
      if (setting.is_refresh_token_enable) {
        // 是否正在刷新 token
        if (!axiosVariable.isRefreshToken) {
          axiosVariable.isRefreshToken = true
          // 1. 如果获取不到刷新令牌，则只能执行登出操作
          if (!getRefreshToken()) {
            return handleAuthorized()
          }
          // 2. 进行刷新访问令牌
          try {
            const refreshTokenRes = await AuthApi.refreshToken()
            // 2.1 刷新成功，则回放队列的请求 + 当前请求
            setToken((await refreshTokenRes).data.data)
            config.headers!.Authorization = 'Bearer ' + getAccessToken()
            axiosVariable.requestList.forEach((cb: any) => {
              cb()
            })
            axiosVariable.requestList = []
            return service(config)
          } catch (e) {
            // 为什么需要 catch 异常呢？刷新失败时，请求因为 Promise.reject 触发异常。
            // 2.2 刷新失败，只回放队列的请求
            axiosVariable.requestList.forEach((cb: any) => {
              cb()
            })
            // 提示是否要登出。即不回放当前请求！不然会形成递归
            return handleAuthorized()
          } finally {
            axiosVariable.requestList = []
            axiosVariable.isRefreshToken = false
          }
        } else {
          // 添加到队列，等待刷新获取到新的令牌
          return new Promise(resolve => {
            axiosVariable.requestList.push(() => {
              config.headers!.Authorization = 'Bearer ' + getAccessToken() // 让每个请求携带自定义token 请根据实际情况自行修改
              resolve(service(config))
            })
          })
        }
      } else {
        // 如果程序没有刷新token功能，则未认证状态直接登出即可
        return handleAuthorized()
      }
      // 请求结果3： 无权限
    } else if (code === setting.result_permit_code) {
      // 如何是无权限，则需要跳转到无权限页面进行提示
      router.push(`/403`)
      return Promise.reject(new Error(msg))
      // 请求结果4： 其它失败结果
    } else {
      // 可忽略的失败结果
      if (setting.ignore_msg.indexOf(msg) !== -1) {
        // 如果是忽略的错误码，直接返回 msg 异常
        return handleAuthorized()
      } else {
        if (config.is_custom_error) {
          return data
        } else {
          ElNotification.error({ title: t('error', 'tip'), message: msg })
          return Promise.reject(new Error(msg))
        }
      }
    }
  },
  (error: AxiosError) => {
    Logger.error('响应拦截error：', error) // for debug
    const appStore = useAppStoreWithOut()
    const { systemConfig } = storeToRefs(appStore)
    const config: CustomInternalAxiosRequestConfig | any = error.config
    if (systemConfig.value && (systemConfig.value.loadingType === 'body' || systemConfig.value.loadingType === 'content') && config && config.is_loading !== false) {
      endLoading()
    }
    if (axios.isCancel(error)) {
      return Promise.reject(error)
    } else {
      setTimeout(() => {
        removePending(config)
      }, 500)
      let { message } = error as AxiosError
      const { t } = useI18n()
      if (message === 'Network Error') {
        message = t('error', 'errorMessage')
      } else if (message.includes('timeout')) {
        message = t('error', 'apiTimeoutMessage')
      } else if (message.includes('Request failed with status code')) {
        message = t('error', 'apiRequestFailed', [message.substr(message.length - 3)])
      } else {
        message = t('error', 'unknownMistake')
      }
      ElMessage.error(message)
      return Promise.reject(error)
    }
  }
)

// 处理错误需要登出时，多个请求会触发多个登出提示的问题
const handleAuthorized = () => {
  const userStore = useUserStoreWithOut()
  const { t } = useI18n()
  if (!axiosVariable.isReSignIn) {
    // 如果已经到重新登录页面则不进行弹窗提示
    if (window.location.href.includes('/Login?redirect=')) {
      return
    }
    axiosVariable.isReSignIn = true
    ElMessageBox.confirm(t('error', 'timeoutMessage'), t('message', 'confirmTitle'), {
      showCancelButton: false,
      closeOnClickModal: false,
      showClose: false,
      confirmButtonText: t('sign', 'reSign'),
      type: 'warning'
    }).then(() => {
      axiosVariable.isReSignIn = false
      userStore.signOut() // 退出登录状态
      // 干掉token后再走一次路由让它过router.beforeEach的校验
      // window.location.href = window.location.href
    })
  }
  return Promise.reject(t('error', 'timeoutMessage'))
}
